﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;

namespace hopchat
{
    public class ruleReport
    {
        public bool success;
        public string report;
        public Luat rule;

        public List<hop_chat> chatThamGia;
        public List<hop_chat> sanpham;

        public ruleReport()
        {
            success = false;
            report = "";
            rule = null; 
            chatThamGia = sanpham = null;
        }
    }

    public class Luat
    {
        private List<hop_chat> Bang;
        private List<hop_chat> Khac;
        private hop_chat Then;
        private string mota;

        public string Mo_Ta
        {
            get { return mota; }
            set { mota = value; }
        }

        protected Luat() { }

        public Luat(XmlElement luat)
        {
            Bang = new List<hop_chat>();
            Khac = new List<hop_chat>();
            XmlNode bang = luat.GetElementsByTagName("Bằng")[0];
            XmlNode khac = luat.GetElementsByTagName("Khác")[0];
            XmlNode then = luat.GetElementsByTagName("THEN")[0];
            XmlDocument document;
            XmlElement temp;

            mota = luat.GetElementsByTagName("Mota")[0].InnerText.Trim();

            for (int i = 0; i < bang.ChildNodes.Count; i++)
            {
                document = new XmlDocument();
                temp = document.CreateElement("hopchat");
                temp.InnerXml = bang.ChildNodes[i].InnerXml;
                document.AppendChild(temp);
                Bang.Add(new hop_chat(document));
            }

            for (int i = 0; i < khac.ChildNodes.Count; i++)
            {
                document = new XmlDocument();
                temp = document.CreateElement("hopchat");
                temp.InnerXml = khac.ChildNodes[i].InnerXml;
                document.AppendChild(temp);
                Khac.Add(new hop_chat(document));
            }

            document = new XmlDocument();
            temp = document.CreateElement("hopchat");
            temp.InnerXml = then.ChildNodes[0].InnerXml;
            document.AppendChild(temp);
            Then = new hop_chat(document);
        }

        public virtual ruleReport Xuly(List<hop_chat> Input){
            //bool result = false;

            ruleReport result = new ruleReport();
            foreach ( hop_chat x in Input){
                bool xet = true;

                string useInReport = x.ToString();

                foreach (hop_chat bang in Bang)
                    if (x != bang)
                        xet = false;
                
                foreach (hop_chat khac in Khac)
                    if (x == khac)
                        xet = false;

                if (xet == true)
                    if (x.ganbang(Then) == true)
                    {
                        result.success = true;
                        result.rule = this;
                        result.report += "Áp dụng luật " + mota + " lên chất " + useInReport + " ta suy ra chất này " + Then.ToString() + "\n";
                    }
            }

            return result;
        }
    }

    public class group21 : Luat
    {
        private TapLuat rule;

        public group21(TapLuat _rule)
        {
            Mo_Ta = "Muối phản ứng với base tạo ra muối mới base mới nếu trong 2 chất tạo ra có chất kết tủa hoặc bay hơi";
            rule = _rule;
        }


        public override ruleReport Xuly(List<hop_chat> input)
        {
            ruleReport result = new ruleReport();
            int c1;
            int c2;

            for (int i = 0; i < input.Count - 1; i++)
                for (int j = i+1; j < input.Count; j++)
                    if (input[i].lowerhopchat(input[i]._tinhchat) == "dễ tan" && input[j].lowerhopchat(input[j]._tinhchat) == "dễ tan")
                    {
                        if (input[i].lowerhopchat(input[i]._loai) == "muối" && input[j].lowerhopchat(input[j]._loai) == "base")
                        {
                            c1 = i;
                            c2 = j;
                        }
                        else if (input[i].lowerhopchat(input[i]._loai) == "base" && input[j].lowerhopchat(input[j]._loai) == "muối")
                        {
                            c1 = j;
                            c2 = i;
                        }
                        else continue;
                        {
                            
                            DonChat a = new DonChat();
                            a.GanBang(input[c1]._thanhphan[0]);
                            DonChat b = new DonChat();
                            b.GanBang(input[c2]._thanhphan[1]);
                            List<DonChat> lst = new List<DonChat>();
                            lst.Add(a);
                            lst.Add(b);
                            hop_chat hc1 = new hop_chat("Base", null, lst);

                            
                            a = new DonChat();
                            a.GanBang(input[c2]._thanhphan[0]);
                            b = new DonChat();
                            b.GanBang(input[c1]._thanhphan[1]);
                            lst = new List<DonChat>();
                            lst.Add(a);
                            lst.Add(b);
                            hop_chat hc2 = new hop_chat("Muối", null , lst);

                            List<hop_chat> lst2 = new List<hop_chat>();
                            lst2.Add(hc1);
                            lst2.Add(hc2);
                            rule.findInfo(lst2);

                            if(lst2[0]._tinhchat != null && lst2[1]._tinhchat !=null)
                                if (lst2[0].lowerhopchat(lst2[0]._tinhchat) != "dễ tan" || lst2[1].lowerhopchat(lst2[1]._tinhchat) != "dễ tan")
                                {
                                    bool check = false;
                                    if (lst2[0].checklist(input))
                                    {
                                        check = true;
                                        input.Add(lst2[0]);
                                    }
                                    if (lst2[1].checklist(input))
                                    {
                                        check = true;
                                        input.Add(lst2[1]);
                                    }
                                    if (check == true)
                                    {
                                        result.success = true;
                                        result.rule = this;
                                        result.chatThamGia = new List<hop_chat>();
                                        result.chatThamGia.Add(input[c1]);
                                        result.chatThamGia.Add(input[c2]);
                                        result.sanpham = lst2;
                                        result.report = "Muối " + input[c1].ToString() + " phản ứng với base " + input[c2].ToString() + " tạo ra muối mới "
                                            + lst2[1].ToString() + " và base mới " + lst2[0].ToString();
                                        return result;
                                    }
                                }
                        }
                    }

            result.success = false;
            return result;
        }
    }

    public class group22 : Luat
    {
        private TapLuat rule;

        public group22(TapLuat _rule)
        {
            Mo_Ta = "Muối phản ứng với acid tạo ra muối mới acid mới nếu trong 2 chất tạo ra có chất kết tủa hoặc bay hơi";
            rule = _rule;
        }


        public override ruleReport Xuly(List<hop_chat> input)
        {
            ruleReport result = new ruleReport();
            int c1; //muoi
            int c2; //acid

            for (int i = 0; i < input.Count - 1; i++)
                for (int j = i + 1; j < input.Count; j++)
                    if ((input[i].lowerhopchat(input[i]._loai) == "acid" && input[i].lowerhopchat(input[i]._tinhchat) == "dễ tan") ||
                        (input[j].lowerhopchat(input[j]._loai) == "acid" && input[j].lowerhopchat(input[j]._tinhchat) == "dễ tan"))
                    {
                        if (input[i].lowerhopchat(input[i]._loai) == "muối" && input[j].lowerhopchat(input[j]._loai) == "acid")
                        {
                            c1 = i;
                            c2 = j;
                        }
                        else if (input[i].lowerhopchat(input[i]._loai) == "acid" && input[j].lowerhopchat(input[j]._loai) == "muối")
                        {
                            c1 = j;
                            c2 = i;
                        }
                        else continue;
                        {
                            
                            DonChat a = new DonChat();
                            a.GanBang(input[c1]._thanhphan[0]);
                            DonChat b = new DonChat();
                            b.GanBang(input[c2]._thanhphan[1]);
                            List<DonChat> lst = new List<DonChat>();
                            lst.Add(a);
                            lst.Add(b);
                            hop_chat hc1 = new hop_chat("Muối", null, lst);

                            
                            a = new DonChat();
                            a.GanBang(input[c2]._thanhphan[0]);
                            b = new DonChat();
                            b.GanBang(input[c1]._thanhphan[1]);
                            lst = new List<DonChat>();
                            lst.Add(a);
                            lst.Add(b);
                            hop_chat hc2 = new hop_chat("Acid", null, lst);

                            List<hop_chat> lst2 = new List<hop_chat>();
                            lst2.Add(hc1);
                            lst2.Add(hc2);
                            rule.findInfo(lst2);

                            //nếu muối cũ ko tan thì acid mới phải ko tan, ko thì 1 trong 2 chất mới phải ko tan
                            if (lst2[0]._tinhchat != null && lst2[1]._tinhchat != null)
                                if (input[c1].lowerhopchat(input[c1]._tinhchat) != "dễ tan" && hc1.lowerhopchat(hc1._tinhchat) != "dễ tan")
                                {
                                    bool check = false;
                                    if (lst2[0].checklist(input))
                                    {
                                        check = true;
                                        input.Add(lst2[0]);
                                    }
                                    if (lst2[1].checklist(input))
                                    {
                                        check = true;
                                        input.Add(lst2[1]);
                                    }
                                    if (check == true)
                                    {
                                        result.success = true;
                                        result.rule = this;
                                        result.chatThamGia = new List<hop_chat>();
                                        result.chatThamGia.Add(input[c1]);
                                        result.chatThamGia.Add(input[c2]);
                                        result.sanpham = lst2;
                                        result.report = "Muối " + input[c1].ToString() + " phản ứng với acid " + input[c2].ToString() + " tạo ra muối mới "
                                            + lst2[0].ToString() + " và acid mới " + lst2[1].ToString();
                                        return result;
                                    }
                                }
                                else if (lst2[0].lowerhopchat(lst2[0]._tinhchat) != "dễ tan" || lst2[1].lowerhopchat(lst2[1]._tinhchat) != "dễ tan")
                                {
                                    bool check = false;
                                    if (lst2[0].checklist(input))
                                    {
                                        check = true;
                                        input.Add(lst2[0]);
                                    }
                                    if (lst2[1].checklist(input))
                                    {
                                        check = true;
                                        input.Add(lst2[1]);
                                    }
                                    if (check == true)
                                    {
                                        result.success = true;
                                        result.rule = this;
                                        result.chatThamGia = new List<hop_chat>();
                                        result.chatThamGia.Add(input[c1]);
                                        result.chatThamGia.Add(input[c2]);
                                        result.sanpham = lst2;
                                        result.report = "Muối " + input[c1].ToString() + " phản ứng với acid " + input[c2].ToString() + " tạo ra muối mới "
                                            + lst2[0].ToString() + " và acid mới " + lst2[1].ToString();
                                        return result;
                                    }
                                }
                        }
                    }

            result.success = false;
            return result;
        }
    }

    public class group23 : Luat
    {
        private TapLuat rule;

        public group23(TapLuat _rule)
        {
            Mo_Ta = "Muối phản ứng với muối tạo ra 2 muối mới nếu trong 2 chất tạo ra có chất kết tủa hoặc bay hơi";
            rule = _rule;
        }


        public override ruleReport Xuly(List<hop_chat> input)
        {
            ruleReport result = new ruleReport();

            for (int i = 0; i < input.Count - 1; i++)
                for (int j = i + 1; j < input.Count; j++)
                    if(input[i].lowerhopchat(input[i]._loai) == "muối" && input[j].lowerhopchat(input[j]._loai) == "muối")
                        if (input[i].lowerhopchat(input[i]._tinhchat) == "dễ tan" && input[j].lowerhopchat(input[j]._tinhchat) == "dễ tan")
                        {
                            {
                            
                                DonChat a = new DonChat();
                                a.GanBang(input[i]._thanhphan[0]);
                                DonChat b = new DonChat();
                                b.GanBang(input[j]._thanhphan[1]);
                                List<DonChat> lst = new List<DonChat>();
                                lst.Add(a);
                                lst.Add(b);
                                hop_chat hc1 = new hop_chat("Muối", null, lst);

                            
                                a = new DonChat();
                                a.GanBang(input[j]._thanhphan[0]);
                                b = new DonChat();
                                b.GanBang(input[i]._thanhphan[1]);
                                lst = new List<DonChat>();
                                lst.Add(a);
                                lst.Add(b);
                                hop_chat hc2 = new hop_chat("Muối", null, lst);

                                List<hop_chat> lst2 = new List<hop_chat>();
                                lst2.Add(hc1);
                                lst2.Add(hc2);
                                rule.findInfo(lst2);

                                if (lst2[0]._tinhchat != null && lst2[1]._tinhchat != null)
                                    if (lst2[0].lowerhopchat(lst2[0]._tinhchat) != "dễ tan" || lst2[1].lowerhopchat(lst2[1]._tinhchat) != "dễ tan")
                                    {
                                        bool check = false;
                                        if (lst2[0].checklist(input))
                                        {
                                            check = true;
                                            input.Add(lst2[0]);
                                        }
                                        if (lst2[1].checklist(input))
                                        {
                                            check = true;
                                            input.Add(lst2[1]);
                                        }
                                        if (check == true)
                                        {
                                            result.success = true;
                                            result.rule = this;
                                            result.chatThamGia = new List<hop_chat>();
                                            result.chatThamGia.Add(input[i]);
                                            result.chatThamGia.Add(input[j]);
                                            result.sanpham = lst2;
                                            result.report = "Muối " + input[i].ToString() + " phản ứng với muối " + input[j].ToString() + " tạo ra muối mới "
                                                + lst2[1].ToString() + " và muối mới " + lst2[0].ToString();
                                            return result;
                                        }
                                    }
                            }
                        }

            result.success = false;
            return result;
        }
    }

    public class group24 : Luat
    {
        private TapLuat rule;

        public group24(TapLuat _rule)
        {
            Mo_Ta = "Acid phản ứng với base tạo ra muối và nước";
            rule = _rule;
        }


        public override ruleReport Xuly(List<hop_chat> input)
        {
            ruleReport result = new ruleReport();
            

            for (int i = 0; i < input.Count - 1; i++)
                for (int j = i + 1; j < input.Count; j++)
                {
                    DonChat a;
                    DonChat b;
                    hop_chat c;
                    List<DonChat> d = new List<DonChat>();
                    List<hop_chat> e = new List<hop_chat>();
                    if ((input[i].lowerhopchat(input[i]._loai) == "acid" && input[j].lowerhopchat(input[j]._loai) == "Base"))
                    {
                        a = new DonChat();
                        b = new DonChat();
                        a.GanBang(input[i]._thanhphan[1]);
                        b.GanBang(input[j]._thanhphan[0]);
                        d.Add(a);
                        d.Add(b);
                        c = new hop_chat("Muối", null, d);
                        e.Add(c);
                        rule.findInfo(e);
                        
                        if (c.checklist(input))
                        {
                            input.Add(c);
                            result.success = true;
                            result.rule = this;
                            result.chatThamGia = new List<hop_chat>();
                            result.chatThamGia.Add(input[i]);
                            result.chatThamGia.Add(input[j]);
                            result.sanpham = new List<hop_chat>();
                            result.sanpham.Add(c);
                            result.report = "Acid " + input[i].ToString() + " phản ứng với Base " + input[j].ToString() + " ra muối " + c.ToString();
                            return result;
                        }
                    }
                    else if (((input[i].lowerhopchat(input[i]._loai) == "base") && (input[j].lowerhopchat(input[j]._loai) == "acid")))
                    {
                        a = new DonChat();
                        b = new DonChat();
                        a.GanBang(input[i]._thanhphan[0]);
                        b.GanBang(input[j]._thanhphan[1]);
                        d.Add(a);
                        d.Add(b);
                        c = new hop_chat("Muối", null, d);
                        e.Add(c);
                        rule.findInfo(e);
                        if (c.checklist(input))
                        {
                            input.Add(c);
                            result.success = true;
                            result.rule = this;
                            result.chatThamGia = new List<hop_chat>();
                            result.chatThamGia.Add(input[i]);
                            result.chatThamGia.Add(input[j]);
                            result.sanpham = new List<hop_chat>();
                            result.sanpham.Add(c);
                            result.report = "Base " + input[i].ToString() + " Phản ứng với Acid " + input[j].ToString() + " ra muối " + c.ToString();
                            return result;
                        }
                    }
                }
            
            result.success = false;
            return result;
        }
    }

    public class TapLuat
    {
        private List<Luat> tapLuat;

        public TapLuat(string path)
        {
            tapLuat = new List<Luat>();
            XmlDocument test = new XmlDocument();
            test.Load(path);
            foreach (XmlNode x in test.GetElementsByTagName("Luat"))
            {
                XmlElement e = test.CreateElement("Luat");
                e.InnerXml = x.InnerXml;
                tapLuat.Add(new Luat(e));
            }
        }

        public static bool checkTapCon(List<hop_chat> A, List<hop_chat> B)
        {
            foreach (hop_chat b in B) //For every member b in collection B
            {
                //b must be in A or else we return false
                if (b.checklist(A)) return false;
            }
            return true;
        }

        public List<ruleReport> findInfo(List<hop_chat> input)
        {
            List<ruleReport> loigiai = new List<ruleReport>();

            bool flag = true;
            
            //Ultilize the group 1 rule to find more information about those chemical
            while (flag)
            {
                flag = false;
                foreach (Luat l in tapLuat)
                {
                    ruleReport kq = l.Xuly(input);
                    if (kq.success == true)
                    {
                        flag = true;
                        loigiai.Add(kq);
                    }
                }
            }

            return loigiai;
        }


        public List<ruleReport> Solve(List<hop_chat> input, List<hop_chat> output)
        {
            List<ruleReport> result = findInfo(input);


            List<Luat> lstLuat = new List<Luat>();
            lstLuat.Add(new group21(this));
            lstLuat.Add(new group22(this));
            lstLuat.Add(new group23(this));
            lstLuat.Add(new group24(this));


            bool flag = true;
            ruleReport lastrp = new ruleReport();
            while (flag)
            {
                flag = false;
                if (  TapLuat.checkTapCon(input, output))
                {
                    //Nếu output là tập con của input
                    flag = false;

                    lastrp.success = true;
                    lastrp.report = "Bài tập này có thể giải được.";
                    result.Add(lastrp);

                    return result;
                }
                foreach (Luat l in lstLuat)
                {
                    ruleReport rp = l.Xuly(input); 
                    if (rp.success) //Luật áp dụng thành công.
                    {
                        result.Add(rp);
                        flag = true;
                        Console.WriteLine(rp.report + "\n\n");
                        if (TapLuat.checkTapCon(input, output))
                        {
                            //Nếu output là tập con của input
                            flag = false;

                            lastrp.success = true;
                            lastrp.report = "Bài tập này có thể giải được.";
                            result.Add(lastrp);

                            return result;
                        }
                    }
                }
            }

            lastrp.success = false;
            lastrp.report = "Bài tập này Không có lời giải.";
            result.Add(lastrp);

            return result;
        }
    }
}
